
#include <BOpenGL>
#include <BMouseEvent>
#include <BKeyEvent>
#include <BViewer>
#include <BCamera>
#include <BRender>
#include <BScene>
#include <BSystem>
#include <BPolygon>

#include "CarverDialog.h"
#include "CarverHandler.h"

CarverHandler::CarverHandler()
{
	this->setName("Carver");

	_brushSize = 50;
	_intensity = 1.0f;
	_dynamic = false;
	_dynamicRatio = 0.5f;
	_pressed = false;
	_shiftDown = false;
	_ctrlDown = false;
	_altDown = false;
	_pointsVisible = true;
	_brushColor.set(200, 0, 0);
	_brushCircle.setSides(24);

	connect(this, Signal_AssetChanged, &CarverHandler::slotAssetChanged);
}
CarverHandler::~CarverHandler()
{
}

void CarverHandler::setBrushSize(int brushSize)
{
	if (_brushSize != brushSize)
	{
		_brushSize = brushSize;
		this->fresh();
	}
}
int CarverHandler::brushSize() const
{
	return _brushSize;
}

void CarverHandler::setIntensity(BReal intensity)
{
	_intensity = intensity;
}
BReal CarverHandler::intensity() const
{
	return _intensity;
}

void CarverHandler::setDynamic(bool dynamic)
{
	_dynamic = dynamic;
}
bool CarverHandler::dynamic() const
{
	return _dynamic;
}

void CarverHandler::setDynamicRatio(BReal ratio)
{
	_dynamicRatio = bClamp(ratio, 0.001, 0.99);
}
BReal CarverHandler::dynamicRatio() const
{
	return _dynamicRatio;
}

void CarverHandler::setPointsVisible(bool pointsVisible)
{
	_pointsVisible = pointsVisible;
	this->fresh();
}
bool CarverHandler::pointsVisible() const
{
	return _pointsVisible;
}

void CarverHandler::catchOnTerrain()
{
	_indexsCatched.reset();

	BMatrix matrix = _terrain->matrix().inverse();
	BCircle circle = _brushCircle * matrix;

	BVectorArray *vertices = _terrain->vertices();
	BIntArray *indices = _terrain->indices();
	if (_pickHit)
	{
		int fi = _pickHit->index();
		int ia = indices->at(fi + 0);
		int ib = indices->at(fi + 1);
		int ic = indices->at(fi + 2);
		int id = indices->at(fi + 3);
		static BIntArray expandIndices;
		expandIndices.reset();
		expandIndices.append(ia);
		expandIndices.append(ib);
		expandIndices.append(ic);
		expandIndices.append(id);
		for (int i = 0; i < _pointInfos.size(); i++)
		{
			_pointInfos[i].state = 0;
		}
		for (int i = 0; i < expandIndices.size(); i++)
		{
			int index = expandIndices[i];
			const BVector &v = vertices->at(index);
			if (circle.around(v))
			{
				_indexsCatched.append(index);
				PointInfo &pinfo = _pointInfos[index];
				pinfo.state = 1;
				for (int li = 0; li < pinfo.links.size(); li++)
				{
					int index = pinfo.links[li];
					if (_pointInfos[index].state == 0 && !expandIndices.contain(index))
					{
						expandIndices.append(index);
					}
				}
			}
			else
			{
				_pointInfos[index].state = -1;
			}
		}
	}
}
void CarverHandler::applyOnTerrain()
{
	BMatrix matrix = _terrain->matrix().inverse();
	BCircle circle = _brushCircle * matrix;

	BVectorArray *vertices = _terrain->vertices();
	BRealTable &elevation = _terrain->elevation();
	for (int i = 0; i < _indexsCatched.size(); i++)
	{
		int index = _indexsCatched[i];
		const BVector &v = vertices->at(index);
		BReal size = circle.radius() - (circle.center() - v).length();
		if (size > 0)
		{
			if (_ctrlDown)
				elevation(index) += -size * _intensity;
			else
				elevation(index) += size * _intensity;
		}
	}
	_terrain->setElevation(elevation);
	_terrain->dirty();
}

void CarverHandler::slotPanelClosed()
{
	emit("finished");
}
void CarverHandler::slotAssetChanged()
{
	_terrain = (BTerrain *)this->asset();
	if (_terrain)
	{
		const BVectorArray *vertices = _terrain->vertices();
		const BVectorArray *normals = _terrain->normals();
		const BIntArray *indices = _terrain->indices();
		_pointInfos.resize(vertices->size());
		for (int i = 0; i < indices->size(); i += 4)
		{
			int ia = indices->at(i + 0);
			int ib = indices->at(i + 1);
			int ic = indices->at(i + 2);
			int id = indices->at(i + 3);
			_pointInfos[ia].addLinks(ib, id);
			_pointInfos[ib].addLinks(ia, ic);
			_pointInfos[ic].addLinks(ib, id);
			_pointInfos[id].addLinks(ia, ic);
		}
	}
}

void CarverHandler::paint(BPainter &painter)
{
}
void CarverHandler::render(BRender &render)
{
	render.setLighting(false);
	render.setDepthTest(false);
	render.setColor(_brushColor);
	render.drawCircle(_brushCircle);

	render.setColor(0, 0, 255, 255);
	render.drawLine(_brushCircle.center(), _brushCircle.center() + _brushCircle.normal() * _brushCircle.radius());

	if (_pointsVisible && _indexsCatched.size())
	{
		render.setPointSize(3);
		render.setPointSmooth(true);
		render.setColor(255, 100, 255);
		BVectorArray *vertices = _terrain->vertices();
		for (int i = 0; i < _indexsCatched.size(); i++)
		{
			int index = _indexsCatched[i];
			BVector v = vertices->at(index) * _terrain->matrix();
			render.drawVertex(v);
		}
	}
}

void CarverHandler::resizeEvent(const BEvent &event)
{
}
void CarverHandler::updateEvent(const BEvent &event)
{
	if (_terrain)
	{
		catchOnTerrain();
		if (_pressed)
			applyOnTerrain();
	}
}

void CarverHandler::keyPressEvent(const BKeyEvent &keyEvent)
{
	if (keyEvent.key() == Key_LShift || keyEvent.key() == Key_RShift)
	{
		_shiftDown = true;
		keyEvent.accept();
	}
	if (keyEvent.key() == Key_LCtrl || keyEvent.key() == Key_RCtrl)
	{
		_ctrlDown = true;
		keyEvent.accept();
	}
	if (keyEvent.key() == Key_Space)
	{
	}
}
void CarverHandler::keyReleaseEvent(const BKeyEvent &keyEvent)
{
	if (keyEvent.key() == Key_LShift || keyEvent.key() == Key_RShift)
	{
		_shiftDown = false;
		keyEvent.accept();
	}
	if (keyEvent.key() == Key_LCtrl || keyEvent.key() == Key_RCtrl)
	{
		_ctrlDown = false;
		keyEvent.accept();
	}
}

void CarverHandler::mousePressEvent(const BMouseEvent &mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		_pressed = true;
		mouseEvent.accept();
	}
}
void CarverHandler::mouseReleaseEvent(const BMouseEvent &mouseEvent)
{
	_pressed = false;
}
void CarverHandler::mouseMoveEvent(const BMouseEvent &mouseEvent)
{
	if (mouseEvent.button() != Button_Right)
	{
		BPoint pos = mouseEvent.pos();
		BPicker *picker = this->viewer()->picker();
		picker->setLine(this->viewer()->line(pos));
		if (_pickHit = picker->apply(_terrain))
		{
			BCamera *camera = this->viewer()->camera();
			float unit = (float)camera->unit(_pickHit->absolutePoint());
			_brushCircle.setRadius(unit * _brushSize * 0.5f);
			_brushCircle.setCenter(_pickHit->absolutePoint());
			_brushCircle.setNormal(camera->up());
			this->fresh();
		}
	}
}

void CarverHandler::mouseClickEvent(const BMouseEvent &mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	mouseEvent.accept();
}
